/*
stolen from http://code.google.com/p/google-mobwrite/downloads/list
*/
/**
 * MobWrite - Real-time Synchronization and Collaboration Service
 *
 * Copyright 2008 Neil Fraser
 * http://code.google.com/p/google-mobwrite/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Record information regarding the current cursor.
 * @return {Object?} Context information of the cursor.
 * @private
 */
function saveCursor(dmp, element) {
  if ('activeElement' in element && !element.activeElement) {
    // Safari specific code.
    // Restoring a cursor in an unfocused element causes the focus to jump.
    return null;
  }
  var padLength = dmp.Match_MaxBits / 2;  // Normally 16.
  var text = element.value;
  var cursor = {};
  if ('selectionStart' in element) {  // W3
    var selectionStart = element.selectionStart;
    var selectionEnd = element.selectionEnd;
    cursor.startPrefix = text.substring(selectionStart - padLength, selectionStart);
    cursor.startSuffix = text.substring(selectionStart, selectionStart + padLength);
    cursor.startPercent = selectionStart / text.length;
    cursor.collapsed = (selectionStart == selectionEnd);
    if (!cursor.collapsed) {
      cursor.endPrefix = text.substring(selectionEnd - padLength, selectionEnd);
      cursor.endSuffix = text.substring(selectionEnd, selectionEnd + padLength);
      cursor.endPercent = selectionEnd / text.length;
    }
  } else {  // IE
    // Walk up the tree looking for this textarea's document node.
    var doc = element;
    while (doc.parentNode) {
      doc = doc.parentNode;
    }
    if (!doc.selection || !doc.selection.createRange) {
      // Not IE?
      return null;
    }
    var range = doc.selection.createRange();
    if (range.parentElement() != element) {
      // Cursor not in this textarea.
      return null;
    }
    var newRange = doc.body.createTextRange();

    cursor.collapsed = (range.text == '');
    newRange.moveToElementText(element);
    if (!cursor.collapsed) {
      newRange.setEndPoint('EndToEnd', range);
      cursor.endPrefix = newRange.text;
      cursor.endPercent = cursor.endPrefix.length / text.length;
      cursor.endPrefix = cursor.endPrefix.substring(cursor.endPrefix.length - padLength);
    }
    newRange.setEndPoint('EndToStart', range);
    cursor.startPrefix = newRange.text;
    cursor.startPercent = cursor.startPrefix.length / text.length;
    cursor.startPrefix = cursor.startPrefix.substring(cursor.startPrefix.length - padLength);

    newRange.moveToElementText(element);
    newRange.setEndPoint('StartToStart', range);
    cursor.startSuffix = newRange.text.substring(0, padLength);
    if (!cursor.collapsed) {
      newRange.setEndPoint('StartToEnd', range);
      cursor.endSuffix = newRange.text.substring(0, padLength);
    }
  }

  // Record scrollbar locations
  if ('scrollTop' in element) {
    cursor.scrollTop = element.scrollTop / element.scrollHeight;
    cursor.scrollLeft = element.scrollLeft / element.scrollWidth;
  }
  
  // alert(cursor.startPrefix + '|' + cursor.startSuffix + ' ' +
  //     cursor.startPercent + '\n' + cursor.endPrefix + '|' +
  //     cursor.endSuffix + ' ' + cursor.endPercent + '\n' +
  //     cursor.scrollTop + ' x ' + cursor.scrollLeft);
  return cursor;
};


/**
 * Attempt to restore the cursor's location.
 * @param {Object} cursor Context information of the cursor.
 * @private
 */
function restoreCursor(dmp, element, cursor) {
  // Set some constants which tweak the matching behaviour.
  // Tweak the relative importance (0.0 = accuracy, 1.0 = proximity)
  dmp.Match_Balance = 0.4;
  // At what point is no match declared (0.0 = perfection, 1.0 = very loose)
  dmp.Match_Threshold = 0.9;

  var padLength = dmp.Match_MaxBits / 2;  // Normally 16.
  var newText = element.value;

  // Find the start of the selection in the new text.
  var pattern1 = cursor.startPrefix + cursor.startSuffix;
  var cursorStartPoint = dmp.match_main(newText, pattern1,
      Math.round(Math.max(0, Math.min(newText.length,
          cursor.startPercent * newText.length - padLength))));
  if (cursorStartPoint !== null) {
    var pattern2 = newText.substring(cursorStartPoint,
                                     cursorStartPoint + pattern1.length);
    //alert(pattern1 + '\nvs\n' + pattern2);
    // Run a diff to get a framework of equivalent indicies.
    var diff = dmp.diff_main(pattern1, pattern2, false);
    cursorStartPoint += dmp.diff_xIndex(diff, cursor.startPrefix.length);
  }

  var cursorEndPoint = null;
  if (!cursor.collapsed) {
    // Find the end of the selection in the new text.
    pattern1 = cursor.endPrefix + cursor.endSuffix;
    cursorEndPoint = dmp.match_main(newText, pattern1,
        Math.round(Math.max(0, Math.min(newText.length,
            cursor.endPercent * newText.length - padLength))));
    if (cursorEndPoint !== null) {
      var pattern2 = newText.substring(cursorEndPoint,
                                       cursorEndPoint + pattern1.length);
      //alert(pattern1 + '\nvs\n' + pattern2);
      // Run a diff to get a framework of equivalent indicies.
      var diff = dmp.diff_main(pattern1, pattern2, false);
      cursorEndPoint += dmp.diff_xIndex(diff, cursor.endPrefix.length);
    }
  }
  
  // Deal with loose ends
  if (cursorStartPoint === null && cursorEndPoint !== null) {
    // Lost the start point of the selection, but we have the end point.
    // Collapse to end point.
    cursorStartPoint = cursorEndPoint;
  } else if (cursorStartPoint === null && cursorEndPoint === null) {
    // Lost both start and end points.
    // Jump to the aproximate percentage point of start.
    cursorStartPoint = Math.round(cursor.startPercent * newText.length);
  }
  if (cursorEndPoint == null) {
    // End not known, collapse to start.
    cursorEndPoint = cursorStartPoint;
  }
  
  // Restore selection.
  if ('selectionStart' in element) {  // W3
    element.selectionStart = cursorStartPoint;
    element.selectionEnd = cursorEndPoint;
  } else {  // IE
    // Walk up the tree looking for this textarea's document node.
    var doc = element;
    while (doc.parentNode) {
      doc = doc.parentNode;
    }
    if (!doc.selection || !doc.selection.createRange) {
      // Not IE?
      return;
    }
    // IE's TextRange.move functions treat '\r\n' as one character.
    var snippet = element.value.substring(0, cursorStartPoint);
    var ieStartPoint = snippet.replace(/\r\n/g, '\n').length;

    var newRange = doc.body.createTextRange();
    newRange.moveToElementText(element);
    newRange.collapse(true);
    newRange.moveStart('character', ieStartPoint);
    if (!cursor.collapsed) {
      snippet = element.value.substring(cursorStartPoint, cursorEndPoint);
      var ieMidLength = snippet.replace(/\r\n/g, '\n').length;
      newRange.moveEnd('character', ieMidLength);
    }
    newRange.select();
  }

  // Restore scrollbar locations
  if ('scrollTop' in cursor) {
    element.scrollTop = cursor.scrollTop * element.scrollHeight;
    element.scrollLeft = cursor.scrollLeft * element.scrollWidth;
  }
};




function getCaretPosition(element){
  var caret = 0;
  if ('selectionStart' in element) {  // W3
    var selectionStart = element.selectionStart;
    var selectionEnd = element.selectionEnd;
    return selectionStart;
  } else {  // IE
    // Walk up the tree looking for this textarea's document node.
    var doc = element;
    while (doc.parentNode) {
      doc = doc.parentNode;
    }
    if (!doc.selection || !doc.selection.createRange) {
      // Not IE?
      return null;
    }
    var range = doc.selection.createRange();
    if (range.parentElement() != element) {
      // Cursor not in this textarea.
      return null;
    }
    var newRange = doc.body.createTextRange();

    var collapsed = (range.text == '');
    newRange.moveToElementText(element);
    if (!collapsed) {
      newRange.setEndPoint('EndToEnd', range);
    }
    newRange.setEndPoint('EndToStart', range);
    var startPrefix = newRange.text;
    return startPrefix.length;
  }
}

/* 
http://stackoverflow.com/questions/29709/how-do-i-get-the-coordinates-of-the-caret-in-text-boxes
*/

function insertAtCaret(myField, myValue) {
    
    /* selecion model - ie */
    if (document.selection) {
        myField.focus();
        sel = document.selection.createRange();
        sel.text = myValue;
    }
    
    /* field.selectionstart/end  firefox */ 
    else if (myField.selectionStart || myField.selectionStart == '0' ) {
        
        var startPos = myField.selectionStart;
        var endPos = myField.selectionEnd;
        myField.value = myField.value.substring(0, startPos)
            + myValue
            + myField.value.substring(endPos, myField.value.length);
        
        myField.selectionStart = startPos + myValue.length;
        myField.selectionEnd = startPos + myValue.length;
        myField.focus();
        } 
        
        // cursor not active/present
    else {
        myField.value += myValue;
    }
}